package per.dg.basicusereflex

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import java.util.*


/**
 * 注意
 * 1、final 修饰的 String 属性，通过反射修改后，直接读取内容，是修改后的内容，但是代码中使用到的地方，仍然是修改之前的内容
 * 1.1、解决方案：用 创建对象（例如：new String("Test")） 或者 用表达式（例如：new String("1") == new String("1")?"":"Test",不能使用基本数据类型）替代
 *
 *
 * 1、反射涉及的类(Class类位于 java.lang 包下；其他的位于 java.lang.reflect 包下)
 * Class类：代表一个类
 * Field类：代表类的成员变量（成员变量也称为类的属性）
 * Method类：代表类的方法
 * Constructor类：代表类的构造方法
 * Array类：提供了动态创建数组，以及访问数组的元素的静态方法
 *
 * 2、反射可以实现的功能
 * 在运行时判断任意一个对象所属的类
 * 在运行时构造任意一个类的对象
 * 在运行时判断任意一个类所具有的成员变量和方法
 * 在运行时调用任意一个对象的方法
 * 动态代理
 *
 * 3、Class 类的主要方法（专属名词：变量、属性、方法、函数、构造方法、构造函数、参数）
 * getName()：获得类的完整名字
 * getFields()：获得类的public类型的属性（包括继承父类的）
 * getDeclaredFields()：获得类的所有属性（只包括自己实现的）
 * getField(String name)：获得类的指定名字的public类型的属性（包括继承父类的）
 * getDeclaredField(String name)：获得类的指定名字的属性（只包括自己实现的）
 * getMethods()：获得类的public类型的方法（包括继承父类的）
 * getDeclaredMethods()：获得类的所有方法（只包括自己实现的）
 * getMethod(String name, Class<?>... parameterTypes)：获得类的public修饰的特定方法，name参数指定方法的名字，parameterTypes参数指定方法的参数类型（包括继承父类的）
 * getDeclaredMethod(String name, Class<?>... parameterTypes)：获得类的特定方法，name参数指定方法的名字，parameterTypes参数指定方法的参数类型（只包括自己实现的）
 * getConstructors()：获得类的public类型的构造方法
 * getDeclaredConstructors()：获得类的所有构造方法
 * getConstructor(Class[] parameterTypes)：获得类的特定构造方法，parameterTypes参数指定构造方法的参数类型
 * newInstance()：通过类的不带参数的构造方法创建这个类的一个对象
 *
 * 4、未实现的方法
 * Constructor、Field、Method、继承自 AccessibleObject
 * 设置 private 修饰的方法、属性为可访问的     AccessibleObject.setAccessible(boolean flag)
 * 获取方法的返回值类型   method.getReturnType()
 * 获取方法的参数类型    method.getParameterTypes()
 */

class MainActivity : AppCompatActivity() {
    private val TAG = "MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        getClassByInstance(Test())

        getClassByClass()

        getClassByString("per.dg.basicusereflex.Test")

        getClassByClassLoader("per.dg.basicusereflex.Test")


        getPublicConstructors()

        getAllConstructors()

        getConstructorOfAppointPublic()

        getAppointConstructor()



        creatInstanceOfNoArgument()

        creatInstanceOfAppointConstructor()



        changePublicField()

        changeFieldOtherThanPublic()

        changeStaticField()

        changeFinalField()

        changeFinalFieldTwo()



        callPublicMethod()

        callStaticMethod()

        callFinalMethod()

        callMethodOtherThanPublic()



        creatInstanceOfPrivateInnerClass()

        creatInstanceOfPrivateStaticInnerClass()

        creatInstanceOfPrivateStaticFinalInnerClass()

    }

    fun getClassByInstance(instance: Test){
        var cl = instance.javaClass
        Log.e(TAG, "getClassByObject: $cl")
    }

    fun getClassByClass(){
        var cl = Test::class.java
        Log.e(TAG, "getClassByClass: $cl" )
    }

    fun getClassByString(path: String){
        var cl = Class.forName(path)
        Log.e(TAG, "getClassByString: $cl" )
    }

    fun getClassByClassLoader(path: String){
        var cl = classLoader.loadClass(path)
        Log.e(TAG, "getClassByClassLoader: $cl" )
    }

    fun getPublicConstructors(){
        var testClas = Test::class.java
        var constructors = testClas.constructors

        for(constructor in constructors){
            Log.e(TAG, "getConstructors: $constructor" )
        }
    }

    fun getAllConstructors(){
        var testClas = Test::class.java
        var constructors = testClas.declaredConstructors

        for(constructor in constructors){
            Log.e(TAG, "getAllConstructors: $constructor" )
        }
    }

    fun getConstructorOfAppointPublic(){
        var testClas = Test::class.java
        var constructor = testClas.getConstructor(String::class.java)
        Log.e(TAG, "getConstructorOfAppointPublic: $constructor" )
    }

    fun getAppointConstructor(){
        var testClas = Test::class.java
        var constructor = testClas.getDeclaredConstructor(String::class.java, 1.javaClass, 1.javaClass)
        Log.e(TAG, "getAppointConstructor: $constructor" )
    }

    fun creatInstanceOfNoArgument(){
        var testClas = Test::class.java
//        创建 Test 无参的实例（必须存在无参的构造方法）
        var test = testClas.newInstance()
        Log.e(TAG, "creatInstanceOfNoArgument: $test" )
    }

    fun creatInstanceOfAppointConstructor(){
        var testClas = Test::class.java
        var constructor = testClas.getDeclaredConstructor(String::class.java, 1.javaClass, 1.javaClass)
        constructor.isAccessible = true
        var test = constructor.newInstance("bin", 2, 2)
        Log.e(TAG, "creatInstanceOfAppointConstructor: $test" )
    }

    fun changePublicField(){
        var testClas = Test::class.java
        var test = testClas.newInstance()
//        获取 Test 类中的 TAG 属性
        var fieldAge = testClas.getField("age")
        Log.e(TAG, "changePublicField: ${fieldAge.get(test)}" )
        fieldAge.set(test, 2)
        Log.e(TAG, "changePublicField: ${fieldAge.get(test)}" )
    }

    fun changeFieldOtherThanPublic(){
        var testClas = Test::class.java
        var test = testClas.newInstance()
//        获取 Test 类中的 result 属性
        var fieldResult = testClas.getDeclaredField("result")
//        public 以外修饰的属性需要设置为可访问
        fieldResult.isAccessible = true
        Log.e(TAG, "changeFieldOtherThanPublic: ${fieldResult.get(test)}" )
        fieldResult.set(test, 2)
        Log.e(TAG, "changeFieldOtherThanPublic: ${fieldResult.get(test)}" )
    }

    fun changeStaticField(){
        var testClas = Test::class.java
        var test = testClas.newInstance()
//        获取 Test 类中的 name 属性
        var fieldName = testClas.getDeclaredField("name")
//        public 以外修饰的属性需要设置为可访问
        fieldName.isAccessible = true
        Log.e(TAG, "changeStaticField: ${fieldName.get(test)}" )
        fieldName.set(test, "2")
        Log.e(TAG, "changeStaticField: ${fieldName.get(test)}" )
    }

    fun changeFinalField(){
        var testClas = Test::class.java
        var test = testClas.newInstance()
//        获取 Test 类中的 TAG 属性
        var fieldTAG = testClas.getDeclaredField("TAG")
//        public 以外修饰的属性需要设置为可访问
        fieldTAG.isAccessible = true
        Log.e(TAG, "changeFinalField: ${fieldTAG.get(test)}" )
        fieldTAG.set(test, "2")
        Log.e(TAG, "changeFinalField: ${fieldTAG.get(test)}" )
    }

    fun changeFinalFieldTwo(){
        var testClas = Test::class.java
        var test = testClas.newInstance()
//        获取 Test 类中的 TAG 属性
        var fieldFinalString = testClas.getDeclaredField("finalString")
//        public 以外修饰的属性需要设置为可访问
        fieldFinalString.isAccessible = true
        Log.e(TAG, "changeFinalFieldTwo: ${fieldFinalString.get(test)}" )
        fieldFinalString.set(test, "2")
        Log.e(TAG, "changeFinalFieldTwo: ${fieldFinalString.get(test)}" )
        test.printFinalString()
    }

    fun callPublicMethod(){
        var testClas = Class.forName("per.dg.basicusereflex.Test")
        var test = testClas.newInstance()
//        获取 Test 类中的 hello 方法(public 修饰的方法)
        var methodHello = testClas.getMethod("hello")
        methodHello.parameterTypes
//        执行 hello 方法
        methodHello.invoke(test)
    }

    fun callMethodOtherThanPublic(){
        var testClas = Class.forName("per.dg.basicusereflex.Test")
        var test = testClas.newInstance()
//        获取 Test 类中的 setName 方法
        var methodSetName = testClas.getDeclaredMethod("setName", "String.javaClass".javaClass)
//        public 以外修饰的方法需要设置可修改
        methodSetName.isAccessible = true
//        执行 setName 方法
        methodSetName.invoke(test, "bin")
    }

    fun callStaticMethod(){
        var testClas = Class.forName("per.dg.basicusereflex.Test")
        var test = testClas.newInstance()
//        获取 Test 类中的 getName 方法
        var methodgetName = testClas.getDeclaredMethod("getName")
//        public 以外修饰的方法需要设置可修改
        methodgetName.isAccessible = true
//        执行 getName 方法
        methodgetName.invoke(test)
//        or
//        methodgetName.invoke(null)
    }

    fun callFinalMethod(){
        var testClas = Class.forName("per.dg.basicusereflex.Test")
        var test = testClas.newInstance()
//        获取 Test 类中的 printClassName 方法
        var methodPrintClassName = testClas.getDeclaredMethod("printClassName")
//        执行 printClassName 方法
        methodPrintClassName.invoke(test)
    }

    fun creatInstanceOfPrivateInnerClass(){
        var cl = Class.forName("per.dg.basicusereflex.Test\$Builder")
        var constructors = cl.declaredConstructors
        for(constructor in constructors){
            Log.e(TAG, "creatInstanceOfPrivateInnerClass: $constructor" )
        }
        var constructor = cl.getDeclaredConstructor(Test::class.java)
        constructor.isAccessible = true
        var builder = constructor.newInstance(Test())
        Log.e(TAG, "creatInstanceOfPrivateInnerClass: $builder" )
    }

    fun creatInstanceOfPrivateStaticInnerClass(){
        var cl = Class.forName("per.dg.basicusereflex.Test\$BuilderTwo")
        var constructors = cl.declaredConstructors
        for(constructor in constructors){
            Log.e(TAG, "creatInstanceOfPrivateStaticInnerClass: $constructor" )
        }
        var constructor = cl.getDeclaredConstructor()
        constructor.isAccessible = true
        var builderTwo = constructor.newInstance()
        Log.e(TAG, "creatInstanceOfPrivateStaticInnerClass: $builderTwo" )
    }

    fun creatInstanceOfPrivateStaticFinalInnerClass(){
        var cl = Class.forName("per.dg.basicusereflex.Test\$BuilderThree")
        var constructors = cl.declaredConstructors
        for(constructor in constructors){
            Log.e(TAG, "creatInstanceOfPrivateStaticFinalInnerClass: $constructor" )
        }
        var constructor = cl.getDeclaredConstructor()
        constructor.isAccessible = true
        var builderTwo = constructor.newInstance()
        Log.e(TAG, "creatInstanceOfPrivateStaticFinalInnerClass: $builderTwo" )
    }
}